package com.luoboduner.moo.info.ui;

import cn.hutool.core.io.unit.DataSizeUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.formdev.flatlaf.FlatDarculaLaf;
import com.formdev.flatlaf.FlatDarkLaf;
import com.formdev.flatlaf.FlatIntelliJLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import com.formdev.flatlaf.intellijthemes.*;
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
import com.formdev.flatlaf.themes.FlatMacLightLaf;
import com.luoboduner.moo.info.App;
import com.luoboduner.moo.info.bean.*;
import com.luoboduner.moo.info.ui.component.TopMenuBar;
import com.luoboduner.moo.info.ui.form.*;
import com.luoboduner.moo.info.util.RestUtil;
import com.luoboduner.moo.info.util.SystemUtil;
import com.luoboduner.moo.info.util.UIUtil;
import com.luoboduner.moo.info.util.UpgradeUtil;
import org.apache.commons.lang3.StringUtils;
import oshi.hardware.*;
import oshi.software.os.*;

import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import java.awt.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * The init Class
 *
 * @author <a href="https://github.com/rememberber">RememBerBer</a>
 * @since 2021/11/09.
 */
public class Init {

  private static final Log logger = LogFactory.get();

  /**
   * font size inti KEY
   */
  private static final String FONT_SIZE_INIT_PROP = "fontSizeInit";

  private static final MonSubjectBo subjectBo = new MonSubjectBo();
  public static MonDeptEquipTypeVo deptAppList;
  private static ScheduledExecutorService uploadService;
  private static ScheduledExecutorService normalService;

  public static int getSuccessNum() {
    return successNum;
  }

  public static int getFailNum() {
    return failNum;
  }

  private static int successNum = 0;
  private static int failNum = 0;

  /**
   * set font for global
   */
  public static void initGlobalFont() {
    if (StringUtils.isEmpty(App.config.getProps(FONT_SIZE_INIT_PROP))) {
      // Adjust the font size according to the DPI
      // Gets the resolution of the screen dpi
      // dell 1920*1080/24 inch =96
      // Xiaomi air 1920*1080/13.3 inch =144
      // Xiaomi air 1366*768/13.3inch =96
      int fontSize = 12;

      // Initialize high-resolution screen font sizes such as Macs
      if (SystemUtil.isMacOs()) {
        fontSize = 15;
      }
      else {
        fontSize = (int) (UIUtil.getScreenScale() * fontSize);
      }
      App.config.setFontSize(fontSize);

      App.config.setProps(FONT_SIZE_INIT_PROP, "true");
      App.config.save();

      TopMenuBar.getInstance().initFontSizeMenu();
    }

    Font font = new Font(App.config.getFont(), Font.PLAIN, App.config.getFontSize());
    FontUIResource fontRes = new FontUIResource(font);
    for (Enumeration<Object> keys = UIManager.getDefaults().keys(); keys.hasMoreElements(); ) {
      Object key = keys.nextElement();
      Object value = UIManager.get(key);
      if (value instanceof FontUIResource) {
        UIManager.put(key, fontRes);
      }
    }

  }

  public static void getDeptEquipAppList() {
    try {
      JSONObject vo = JSONUtil.parseObj(RestUtil.get("/agent/deptList")).getJSONObject("data");
      deptAppList = JSONUtil.toBean(vo, MonDeptEquipTypeVo.class);
      if (StrUtil.isBlank(deptAppList.getFreq())) {
        deptAppList.setFreq("120");
      }

      if (!App.config.getFreq().equals(deptAppList.getFreq())){
        stopStateSchedule();
        App.config.setFreq(deptAppList.getFreq());
        App.config.save();
        startStateSchedule();
      }
    }
    catch (Exception e) {
      logger.error("/agent/deptList出错：" + e.getMessage());
      deptAppList = null;
    }
  }

  public static void startStateSchedule() {
    int freq = 120;
    try {
      freq = Integer.parseInt(App.config.getFreq());
    } catch (Exception ignored) {
    }
    normalService = Executors.newSingleThreadScheduledExecutor();
    normalService.scheduleAtFixedRate(Init::uploadSubjectState, 1, freq, TimeUnit.SECONDS);
  }

  public static void stopStateSchedule() {
    if (normalService != null) {
      normalService.shutdownNow();
    }
    normalService = null;
  }

  public static void startUploadSchedule() {
    uploadService = Executors.newSingleThreadScheduledExecutor();
    uploadService.scheduleAtFixedRate(() -> Init.uploadSubjectInfo(false), 1, 60, TimeUnit.MINUTES);
  }

  public static void stopUploadSchedule() {
    if (uploadService != null) {
      uploadService.shutdownNow();
    }
    uploadService = null;
  }

  public static void getAppList() {
    try {
      JSONObject vo = JSONUtil.parseObj(RestUtil.get("/agent/appList"));
      JSONArray list = vo.getJSONArray("data");
      List<MonApp> a = JSONUtil.toList(list, MonApp.class);
      deptAppList.setApps(a);
    }
    catch (Exception e) {
      logger.error("/agent/deptList出错：" + e.getMessage());
      deptAppList.setApps(null);
    }

  }


  public static void uploadSubjectInfo(boolean immediate) {
    if (!App.config.getEquipId().isEmpty()) {
      getDeptEquipAppList();
//      getAppList();
      MonSubject subject = new MonSubject();
      subjectBo.setSubject(subject);
      List<MonDiskInfo> diskInfos = new ArrayList<>();
      subjectBo.setDiskInfos(diskInfos);
      List<MonNetInfo> netInfos = new ArrayList<>();
      subjectBo.setNetInfos(netInfos);
      List<MonGraphicsInfo> graphicsInfos = new ArrayList<>();
      subjectBo.setGraphicsInfos(graphicsInfos);
      List<MonMemInfo> memInfos = new ArrayList<>();
      subjectBo.setMemInfos(memInfos);

      subject.setId(App.config.getEquipId());
      subject.setDeptId(Long.parseLong(App.config.getDeptId()));
      subject.setSubjectId(App.config.getSubjectId());
      subject.setAddress(App.config.getAddress());
      subject.setSubjectName(App.config.getSubjectName());

      OperatingSystem os = App.si.getOperatingSystem();

      subject.setProcessNum(os.getProcesses().size());
      subject.setOsManufacturer(os.getManufacturer());
      subject.setOsFamily(os.toString());
      subject.setOsVersion(os.getVersionInfo().getVersion());

      ComputerSystem computerSystem = App.si.getHardware().getComputerSystem();
      subject.setCompManufacturer(computerSystem.getManufacturer());
      subject.setCompModel(computerSystem.getModel());
      subject.setCompSerial(computerSystem.getSerialNumber());
      subject.setCompUuid(computerSystem.getHardwareUUID());

      CentralProcessor processor = App.si.getHardware().getProcessor();
      CentralProcessor.ProcessorIdentifier processorIdentifier = processor.getProcessorIdentifier();

      if (!immediate) {
        subject.setCpuPer((int) CpuForm.getCpuUsage(processor));
      }
      subject.setCpuCoreNum(processor.getLogicalProcessorCount());
      subject.setCpuName(processorIdentifier.getName());
      subject.setCpuIdentifier(processorIdentifier.getIdentifier());
      subject.setCpuId(processorIdentifier.getProcessorID());
      subject.setCpuFreq(new BigDecimal(processorIdentifier.getVendorFreq()).divide(new BigDecimal(1000000000), 2, RoundingMode.HALF_UP) + " GHz");

      NetworkParams networkParams = os.getNetworkParams();
      subject.setNetHostname(networkParams.getHostName());
      subject.setNetIpv4(networkParams.getIpv4DefaultGateway());
      subject.setNetIpv6(networkParams.getIpv6DefaultGateway());
      subject.setNetDns(Arrays.toString(networkParams.getDnsServers()));

      /*  生成网络详情  */
      List<NetworkIF> networkIfList = App.si.getHardware().getNetworkIFs(true);
      int iNet = 0;
      for (NetworkIF networkIf : networkIfList) {
        String[] ipV4s = networkIf.getIPv4addr();
        String ipV4 = ipV4s.length > 0 ? ipV4s[0] : "";

        String[] ipV6s = networkIf.getIPv6addr();
        String ipV6 = ipV6s.length > 0 ? ipV6s[0] : "";

        if (!ipV4.isEmpty() && !ipV4.equals("127.0.0.1")) {
          MonNetInfo netInfo = new MonNetInfo();
          netInfo.setSubjectId(subject.getId());
          netInfo.setName(networkIf.getName());
          netInfo.setMac(networkIf.getMacaddr());
          netInfo.setIpv4(ipV4);
          netInfo.setIpv6(ipV6);
          netInfo.setSpeed(String.valueOf(networkIf.getSpeed()));
          netInfo.setOrderNum(++iNet);
          netInfo.setDeptId(Long.parseLong(App.config.getDeptId()));
          netInfos.add(netInfo);
        }
      }

      NetworkForm.initNetworkSpeed();
      subject.setNetUp(NetworkForm.uploadSpeed);
      subject.setNetUp(NetworkForm.downloadSpeed);

      GlobalMemory globalMemory = App.si.getHardware().getMemory();
      subject.setMemTotal(globalMemory.getTotal());
      subject.setMemPage(globalMemory.getPageSize());
      subject.setMemUsed(globalMemory.getTotal() - globalMemory.getAvailable());
      subject.setMemPer((int) (subject.getMemUsed() * 100 / subject.getMemTotal()));
      /*  生成内存详情  */
      List<PhysicalMemory> physicalMemories = globalMemory.getPhysicalMemory();
      for (int i = 0; i < physicalMemories.size(); i++) {
        MonMemInfo memInfo = new MonMemInfo();
        PhysicalMemory physicalMemory = physicalMemories.get(i);
        memInfo.setSubjectId(subject.getId());
        memInfo.setManufacturer(physicalMemory.getManufacturer());
        memInfo.setBankLabel(physicalMemory.getBankLabel());
        memInfo.setCapacity(DataSizeUtil.format(physicalMemory.getCapacity()));
        memInfo.setMemType(physicalMemory.getMemoryType());
        memInfo.setSpeed(new BigDecimal(physicalMemory.getClockSpeed()).divide(new BigDecimal(1000000), 0, RoundingMode.HALF_UP) + "MHz");
        memInfo.setOrderNum(i + 1);
        memInfo.setDeptId(Long.parseLong(App.config.getDeptId()));
        memInfos.add(memInfo);
      }

      /*  生成显卡详情  */
      List<GraphicsCard> graphicsCards = App.si.getHardware().getGraphicsCards();

      for (int i = 0; i < graphicsCards.size(); i++) {
        GraphicsCard graphicsCard = graphicsCards.get(i);
        MonGraphicsInfo graphicsInfo = new MonGraphicsInfo();
        graphicsInfo.setVendor(graphicsCard.getVendor());
        graphicsInfo.setSubjectId(subject.getId());
        graphicsInfo.setDeviceId(graphicsCard.getDeviceId());
        graphicsInfo.setVersion(graphicsCard.getVersionInfo());
        graphicsInfo.setName(graphicsCard.getName());
        graphicsInfo.setVram(DataSizeUtil.format(graphicsCard.getVRam()));
        graphicsInfo.setOrderNum(i + 1);
        graphicsInfo.setDeptId(Long.parseLong(App.config.getDeptId()));
        graphicsInfos.add(graphicsInfo);
      }

      FileSystem fileSystem = App.si.getOperatingSystem().getFileSystem();
      List<OSFileStore> fileStores = fileSystem.getFileStores();

      long usable = 0L;
      long total = 0L;
      for (OSFileStore store : fileStores) {
        MonDiskInfo diskInfo = new MonDiskInfo();
        diskInfo.setSubjectId(App.config.getEquipId());
        diskInfo.setSize(store.getTotalSpace());
        diskInfo.setAvail(store.getUsableSpace());
        diskInfo.setUsed(diskInfo.getSize() - diskInfo.getAvail());
        diskInfo.setUsePer(Math.round((float) diskInfo.getUsed() / diskInfo.getSize() * 100));
        diskInfo.setFileSystem(store.getName());
        diskInfo.setDeptId(Long.parseLong(App.config.getDeptId()));
        diskInfos.add(diskInfo);

        usable += store.getUsableSpace();
        total += store.getTotalSpace();

      }
      subject.setDiskUsed(usable);
      subject.setDiskTotal(total);
      subject.setDiskPer((int) (usable * 100 / total));

      try {
        RestUtil.post("/agent/subjectBo" + (immediate ? "" : "/addList"), new JSONObject(subjectBo), "subjectBo");
      }
      catch (Exception e) {
        logger.error(e.getMessage());
      }
    }
  }

  public static void uploadSubjectState() {
    if (!App.config.getEquipId().isEmpty()) {
      if (ObjectUtil.isEmpty(deptAppList)) {
        getDeptEquipAppList();
      }
      if (ObjectUtil.isNotEmpty(deptAppList)) {
        MonStateBo stateBo = new MonStateBo();

        MonSubjectState subjectState = new MonSubjectState();
        subjectState.setDeptId(Long.parseLong(App.config.getDeptId()));
        stateBo.setSubjectState(subjectState);
        List<MonAppState> list = new ArrayList<>();
        stateBo.setAppStates(list);

        OperatingSystem os = App.si.getOperatingSystem();
        List<OSProcess> processes = os.getProcesses();
        subjectState.setProcessNum(processes.size());

        if (ObjectUtil.isNotEmpty(deptAppList.getApps())) {
          long totalMem = App.si.getHardware().getMemory().getTotal();
          int cpuCount = App.si.getHardware().getProcessor().getLogicalProcessorCount();

          deptAppList.getApps().forEach(obj -> {
            double cpuPer = 0;
            double memPer = 0;
            long mem = 0L;
            int threadCount = 0;
            MonAppState appState = new MonAppState();

            for (OSProcess p : processes) {
              if (p.getName().equals(obj.getAppName())) {
                threadCount += p.getThreadCount();
                cpuPer += 100d * p.getProcessCpuLoadBetweenTicks(p) / cpuCount;
                memPer += 100d * p.getResidentSetSize() / totalMem;
                mem += p.getResidentSetSize();
              }
            }
            appState.setAppId(obj.getId());
            appState.setCpuPer(cpuPer);
            appState.setMemPer(memPer);
            appState.setMemCapacity(mem);
            appState.setThreadCount(threadCount);
            appState.setSubjectId(App.config.getEquipId());
            appState.setDeptId(Long.parseLong(App.config.getDeptId()));

            list.add(appState);
          });

        }

        CentralProcessor processor = App.si.getHardware().getProcessor();

        subjectState.setCpuPer((int) CpuForm.getCpuUsage(processor));

        NetworkForm.initNetworkSpeed();
        subjectState.setNetUp(NetworkForm.uploadSpeed);
        subjectState.setNetDown(NetworkForm.downloadSpeed);

        GlobalMemory globalMemory = App.si.getHardware().getMemory();
        subjectState.setMemTotal(globalMemory.getTotal());
        subjectState.setMemUsed(globalMemory.getTotal() - globalMemory.getAvailable());
        subjectState.setMemPer((int) (subjectState.getMemUsed() * 100 / subjectState.getMemTotal()));

        FileSystem fileSystem = App.si.getOperatingSystem().getFileSystem();
        List<OSFileStore> fileStores = fileSystem.getFileStores();

        long usable = 0L;
        long total = 0L;
        for (OSFileStore store : fileStores) {
          usable += store.getUsableSpace();
          total += store.getTotalSpace();
        }
        subjectState.setDiskUsed(usable);
        subjectState.setDiskTotal(total);
        subjectState.setDiskPer((int) (usable * 100 / total));

        subjectState.setSubjectId(App.config.getEquipId());

        try {
          RestUtil.post("/agent/stateBo/addList", new JSONObject(stateBo), "stateBo");
          successNum++;
          OverviewForm.getInstance().setServerOnline(true);
        }
        catch (Exception e) {
          failNum++;
          OverviewForm.getInstance().setServerOnline(false);
          logger.error(e.getMessage());
        }
      }
    }
  }


  /**
   * Other initialization
   */
  public static void initOthers() {

  }

  /**
   * init look and feel
   */
  public static void initTheme() {

    try {
      switch (App.config.getTheme()) {
        case "System Default":
          UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
          break;
        case "Flat Light":
          FlatLightLaf.setup();
          break;
        case "Flat IntelliJ":
          FlatIntelliJLaf.setup();
          break;
        case "Flat Dark":
          FlatDarkLaf.setup();
          break;
        case "Dark purple":
          FlatDarkPurpleIJTheme.setup();
          break;
        case "IntelliJ Cyan":
          FlatCyanLightIJTheme.setup();
          break;
        case "IntelliJ Light":
          FlatLightFlatIJTheme.setup();
          break;
        case "Xcode-Dark":
          FlatXcodeDarkIJTheme.setup();
          break;
        case "Vuesion":
          FlatVuesionIJTheme.setup();
          break;
        case "Flat macOS Light":
          FlatMacLightLaf.setup();
          break;
        case "Flat macOS Dark":
          FlatMacDarkLaf.setup();
          break;
        default:
          FlatDarculaLaf.setup();
      }

      if (UIUtil.isDarkLaf()) {
//                FlatSVGIcon.ColorFilter.getInstance().setMapper(color -> color.brighter().brighter());
      }
      else {
        FlatSVGIcon.ColorFilter.getInstance().setMapper(color -> color.darker().darker());
//                SwingUtilities.windowForComponent(App.mainFrame).repaint();
      }

      if (App.config.isUnifiedBackground()) {
        UIManager.put("TitlePane.unifiedBackground", true);
      }

      // top menubar background
      UIManager.put("PopupMenu.background", UIManager.getColor("Panel.background"));
      // arrow type
      UIManager.put("Component.arrowType", "chevron");
    }
    catch (Exception e) {
      logger.error(e);
    }
  }

  /**
   * init all tab
   */
  public static void initAllTab() {

    ThreadUtil.execute(OverviewForm::init);
    ThreadUtil.execute(DetailForm::init);
    ThreadUtil.execute(MemoryForm::init);
    ThreadUtil.execute(CpuForm::init);
    ThreadUtil.execute(NetworkForm::init);
    ThreadUtil.execute(UsbForm::init);
    ThreadUtil.execute(VariablesForm::init);
    ThreadUtil.execute(ProcessesForm::init);
    ThreadUtil.execute(DiskForm::init);
    ThreadUtil.execute(PowerSourceForm::init);

    // Check the new version
    if (App.config.isAutoCheckUpdate()) {
      ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(1);
      threadPoolExecutor.scheduleAtFixedRate(() -> UpgradeUtil.checkUpdate(true), 0, 24, TimeUnit.HOURS);
    }
  }

  public static boolean configSucceed() {
    return StrUtil.isNotBlank(App.config.getServerIp()) &&
      StrUtil.isNotBlank(App.config.getPort()) &&
      StrUtil.isNotBlank(App.config.getDeptId()) &&
      StrUtil.isNotBlank(App.config.getSubjectId());
  }

  public static void startSchedules() {
    CpuForm.startSchedule();
    MemoryForm.startSchedule();
    NetworkForm.startSchedule();
    PowerSourceForm.startSchedule();

  }

  public static void stopSchedules() {
    CpuForm.stopSchedule();
    MemoryForm.stopSchedule();
    NetworkForm.stopSchedule();
    PowerSourceForm.startSchedule();
  }

  public static void showMainFrame() {
    App.mainFrame.setVisible(true);
    if (App.mainFrame.getExtendedState() == Frame.ICONIFIED) {
      App.mainFrame.setExtendedState(Frame.NORMAL);
    }
    else if (App.mainFrame.getExtendedState() == 7) {
      App.mainFrame.setExtendedState(Frame.MAXIMIZED_BOTH);
    }
    App.mainFrame.requestFocus();
  }

  public static void shutdown() {
    App.mainFrame.dispose();
    System.exit(0);
  }
}
